import axios from 'axios'
import APPConfig from '@/config'
import Vue from 'vue'
import store from '@/store'
import MxLoading from '@/components/mx-loading'
import jsonp from 'jsonp'

Vue.use(MxLoading)
const SUCCESS_CODE = 200
const FAIL_CODE = 500

/**
 * @param api -Object：{url - 请求链接, type - 请求方法}； -String：url,请求方法默认为get
 * @param url -String {url - 请求链接}； -String：url,请求方法默认为get
 * @param method -String {type - 请求方法}； -String：,请求方法默认为get， 可以是post,jsonp
 * @param params 请求参数 Object
 * @param beforeSend 请求前回调函数， Function: 回调参数为config
 * @param complete 请求成功(response.status === 200)回调函数, Function: 回调参数为response.data
 * @param headers 请求头，Object
 * @param baseURL 与api组成完整的链接，String
 * @param timeout 超时时间， Number
 * @param showLoading 是否需要Loading, Boolean
 * @returns {AxiosPromise}
 */
let httpService = (function() {
  let axiosService = null
  let showLoading = true
  let single = function() {
    // 如果不是这个单例创建单例模式
    if (!axiosService) {
      axiosService = axios.create()
      // 请求拦截器
      axiosService.interceptors.request.use(config => {
        showLoading = config.showLoading || true
        if (showLoading) {
          // show Loading
          Vue.$mxLoading.show()
        }
        // `transformRequest` 允许在向服务器发送前，修改请求数据
        // 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
        // 后面数组中的函数必须返回一个字符串，或 ArrayBuffer，或 Stream
        // 这里对post json 进行对象转字符串处理，可以添加其他处理
        if (config.method.toUpperCase() === 'POST' && config.headers['Content-Type'] === 'application/json') {
          config.transformRequest = [function (data) {
            return JSON.stringify(data)
          }]
        }
        // `params` 是即将与请求一起发送的 URL 参数
        // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
        // config.params = config.params || params
        // `data` 是作为请求主体被发送的数据
        // 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
        // 在没有设置 `transformRequest` 时，必须是以下类型之一：
        // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
        // - 浏览器专属：FormData, File, Blob
        // - Node 专属： Stream
        // if (config.method.toUpperCase() === 'POST' || config.method.toUpperCase() === 'PUT' || config.method.toUpperCase() === 'PATCH') {
        //   config.data = config.data || data
        // }

        if (config.beforeSend && config.beforeSend instanceof Function) {
          config.beforeSend(config)
        }
        // 当配置里面开启了token转换则进行token替换，否则直接返回token
        // 当有token且token存在时间不超过1个小时的时候
        // 直接在headers上面加token，否则
        // 重新获取一次用户信息，然后再在headers上面加token
        if (APPConfig.IsTokenTransform) {
          return new Promise(resolve => {
            // 这里异步处理设置token
            let userInfo = store.getters.getUser
            if (!store.dispatch('isTimeOut')) {
              if (config.headers) {
                Object.keys(config.headers).forEach(header => {
                  if (config.headers[header] === APPConfig.tokenPlaceholder) {
                    config.headers[header] = userInfo[APPConfig.tokenKey]
                  }
                })
              }
              if (config.params) {
                Object.keys(config.params).forEach(param => {
                  if (config.params[param] === APPConfig.tokenPlaceholder) {
                    config.params[param] = userInfo[APPConfig.tokenKey]
                  }
                })
              }
              resolve(config)
            } else {
              store.dispatch('getMxUserInfo').then((newUserInfo) => {
                if (config.headers) {
                  Object.keys(config.headers).forEach(header => {
                    if (config.headers[header] === APPConfig.tokenPlaceholder) {
                      config.headers[header] = newUserInfo[APPConfig.tokenKey]
                    }
                  })
                }
                if (config.params) {
                  Object.keys(config.params).forEach(param => {
                    if (config.params[param] === APPConfig.tokenPlaceholder) {
                      config.params[param] = newUserInfo[APPConfig.tokenKey]
                    }
                  })
                }
                resolve(config)
              })
            }
          })
        } else {
          // 这个如果不做token替换则直接返回不做后续处理
          return config
        }
      }, (error) => {
        if (showLoading) {
          Vue.$mxLoading.hide()
        }
        return Promise.reject(error)
      })

      // 响应拦截器
      axiosService.interceptors.response.use((res) => {
        if (showLoading) {
          Vue.$mxLoading.hide()
        }

        const { data } = res
        if (+data.code === FAIL_CODE) {
          return Promise.reject(data)
        }
        return data
      }, (err) => {
        if (showLoading) {
          Vue.$mxLoading.hide()
        }
        return Promise.reject(err)
      })
    }

    return axiosService
  }

  return ({
    api,
    url,
    method,
    beforeSend,
    params,
    data,
    headers,
    timeout,
    showLoading = true
  }) => {
    let configTimeout = timeout || APPConfig.defaultRequsetParams.timeout
    let configHeaders = headers || APPConfig.defaultRequsetParams.headers
    let configApi = ''
    let configType = ''
    let service = single()

    if (api) {
      if (typeof api === 'object') {
        configApi = api.url
        configType = api.type || 'GET'
      } else {
        configApi = api
        configType = 'GET'
      }
    } else {
      configApi = url || ''
      configType = method || 'GET'
    }

    if (configApi === '') {
      return new Promise((resolve, reject) => {
        if (showLoading) {
          Vue.$mxLoading.hide()
        }
        reject(new Error('url is empty!'))
      })
    } else if (configType.toUpperCase() === 'JSONP') {
      return new Promise((resolve, reject) => {
        jsonp(configApi, {
          param: params && JSON.stringify(params),
          configTimeout
        }, (err, data) => {
          if (err) {
            console.log(err)
            reject(err)
          }
          resolve(data)
        })
      })
    } else {
      // 合并请求参数，发起请求
      return service({
        url: configApi,
        method: configType.toUpperCase(),
        headers: configHeaders,
        timeout: configTimeout,
        params,
        data,
        beforeSend,
        showLoading
      })
    }
  }
})()

export default httpService
